﻿using System;

namespace IslandUnit.Providers
{
    /// <summary>
    /// IoC container provider for Ninject.
    /// </summary>
    public class NinjectProvider : ContainerProviderBase
    {
        /// <summary>
        /// Initialize the container.
        /// </summary>
        public override object InitializeContainer()
        {
            // We are doing this, using reflection:
            // IKernel kernel = new StandardKernel();

            return ReflectionHelper.New(ProviderRepository.Instance().ContainerType);
        }

        /// <summary>
        /// Binds the dependency interface to the mocked object.
        /// </summary>
        /// <typeparam name="TDependency">The type of the dependency.</typeparam>
        /// <param name="container">The IoC container.</param>
        /// <param name="mockedObject">The mocked object.</param>
        public override void Bind<TDependency>(object container, TDependency mockedObject)
        {
            // We are doing this, using reflection:
            // IKernel k = (StandardKernel)container;
            // k.Bind(typeof(TDependency)).ToMethod(m => mockedObject);
            // But we have to do in two steps

            // k.Bind(typeof(TDependency))
            object bind = ReflectionHelper.InvokeMethod<TDependency>(container, "Rebind", null, null);

            // .ToMethod(m => mockedObject);
            var func = new Func<object, TDependency>(m => mockedObject);
            ReflectionHelper.InvokeMethod(bind, "ToMethod", new object[] { func });
        }

        /// <summary>
        /// Get an object of the type T with dependencies from container.
        /// </summary>
        public override T Get<T>(object container)
        {
            // We are doing this, using reflection:
            // IKernel k = (StandardKernel)container;
            // k.Get<TDependency>();
            Type extensionsType = ProviderRepository.Instance().ContainerAssembly.GetType("Ninject.ResolutionExtensions");
            Type parameterType = ProviderRepository.Instance().ContainerAssembly.GetType("Ninject.Parameters.IParameter[]");

            return (T)ReflectionHelper.InvokeStaticMethod<T>(extensionsType, "Get", new[] { "Ninject.Syntax.IResolutionRoot", "Ninject.Parameters.IParameter[]" }, new[] { container, ReflectionHelper.New(parameterType, new object[] { 0 }) });
        }
    }
}
